import Express from 'express';
import qs from 'qs';

import webpack from 'webpack';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
import webpackConfig from '../webpack.config';

import React from 'react';
import { renderToString } from 'react-dom/server';
import { Provider } from 'react-redux';

import configureStore from '../common/store/configureStore';
import App from '../common/containers/App';
import { fetchCounter } from '../common/api/counter';

const app = new Express();
const port = 3000;


const compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler, {
    noInfo: true,
    publicPath: webpackConfig.output.publicPath
}));
app.use(webpackHotMiddleware(compiler));

// 接受HTML字符串和state作为参数，返回一个完整的HTML页面
function renderFullPage(html, initialState) {

    return `
    <!doctype html>
    <html>
      <head>
        <title>Redux Universal Example</title>
      </head>
      <body>
        <p>初始化状态: ${JSON.stringify(initialState)}</p>
        <div id="app">${html}</div>
        
        <script>
          window.__INITIAL_STATE__ = ${JSON.stringify(initialState)}
        </script>        
<!-- 客户端渲染的执行脚本
    客户端渲染不需要写更多的代码，因为Universal渲染的意义就是使用同一套代码。
    所以只需要使用render()方法将公用的组件和state 渲染到指定 DOM 中即可
-->
        <script src="/static/bundle.js"></script>
      </body>
    </html>
    `;
}

// Express 中间件
// Express中间件是Express框架安置在收到请求和发出响应之间的一个处理函数。
// 通常情况下，Express中间件会 在每次收到请求时被触发。
function handleRender(req, res) {

    // 在中间件函数被触发时，首先执行 fetchCounter() 获取数据。
    fetchCounter(apiResult => {
        // 拿到数据后，并不直接将其作为初始化 state 的 counter 值，而是判断是否有查询字符串。
        // 如果有，则将查询字符串的数据作为counter，否则才用fetchCounter()拿到的数据作为counter。

        //qs是个npm包，用于解析查询字符串
        const params = qs.parse(req.query);
        const counter = parseInt(params.counter, 10) || apiResult || 0;

        // 初始化state后，创建store实例。
        const initialState = { counter };
        const store = configureStore(initialState);

        // 使用 renderToString 方法将组件渲染成字符串
        const html = renderToString(
            // 然后使用Provider组件给整个程序提供store，并将其与App组件一起渲染成字符串。
            <Provider store={store}>
                <App />
            </Provider>
        );

        const finalState = store.getState();

        //renderFullPage 生成完整html页面，并返回给浏览器
        res.send(renderFullPage(html, finalState));
    });
}

// 使用app.use(handleRender) 将中间件 handleRender 连入 app 服务器
app.use(handleRender);

app.listen(port, (error) => {
    if (error) {
        console.error(error);
    } else {
        console.info(`==> 🌎  Listening on port ${port}. Open up http://localhost:${port}/ in your browser.`);
    }
});
